package com.ftg.learn.service.impl;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.comm.SignVersion;
import com.ftg.learn.service.OssUploadService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

@Service
public class OssUploadServiceImpl implements OssUploadService {
    // 从配置文件注入OSS参数（避免硬编码）
    @Value("${oss.access-key-id}")
    private String accessKeyId;

    @Value("${oss.access-key-secret}")
    private String accessKeySecret;

    @Value("${oss.endpoint}")
    private String endpoint;

    @Value("${oss.region}")
    private String region;

    @Value("sunyubo")
    private String bucketName;

    @Value("${oss.image-dir}")
    private String imageDir;

    @Value("${oss.audio-dir}")
    private String audioDir;

    @Value("${oss.max-file-size}")
    private long maxFileSize;

    // 允许的图片MIME类型（限制仅支持常见图片格式）
    private static final List<String> ALLOWED_IMAGE_TYPES = Arrays.asList(
            "image/jpeg", "image/png", "image/gif", "image/bmp", "image/webp"
    );

    // 允许的音频MIME类型（限制仅支持常见音频格式）
    private static final List<String> ALLOWED_AUDIO_TYPES = Arrays.asList(
            "audio/mpeg", "audio/wav", "audio/mp4", "audio/aac", "audio/flac"
    );

    /**
     * 上传图片实现
     */
    @Override
    public Map<String, Object> uploadImage(MultipartFile file) {
        // 调用通用上传方法（指定图片目录、允许类型、文件描述）
        return commonUpload(file, imageDir, ALLOWED_IMAGE_TYPES, "图片");
    }

    /**
     * 上传音频实现
     */
    @Override
    public Map<String, Object> uploadAudio(MultipartFile file) {
        // 调用通用上传方法（指定音频目录、允许类型、文件描述）
        return commonUpload(file, audioDir, ALLOWED_AUDIO_TYPES, "音频");
    }

    /**
     * 通用上传方法：抽取公共逻辑，避免图片/音频上传代码重复
     * @param file 上传文件
     * @param storageDir OSS存储目录（images/ 或 audios/）
     * @param allowedTypes 允许的文件类型列表
     * @param fileTypeDesc 文件类型描述（用于异常提示，如"图片"、"音频"）
     * @return 上传结果Map
     */
    private Map<String, Object> commonUpload(MultipartFile file, String storageDir, List<String> allowedTypes, String fileTypeDesc) {
        Map<String, Object> result = new HashMap<>();

        try {
            // 1. 校验文件是否为空
            if (file.isEmpty()) {
                result.put("success", false);
                result.put("message", fileTypeDesc + "文件不能为空");
                return result;
            }

            // 2. 校验文件大小（不超过配置的100MB）
            if (file.getSize() > maxFileSize) {
                result.put("success", false);
                result.put("message", fileTypeDesc + "文件大小不能超过100MB");
                return result;
            }

            // 3. 校验文件类型（仅允许配置的类型）
            String contentType = file.getContentType();
            if (contentType == null || !allowedTypes.contains(contentType)) {
                String allowedTypesStr = String.join(", ", allowedTypes.stream()
                        .map(type -> type.split("/")[1]) // 提取后缀（如image/png -> png）
                        .toList());
                result.put("success", false);
                result.put("message", "不支持的" + fileTypeDesc + "类型，允许：" + allowedTypesStr);
                return result;
            }

            // 4. 生成唯一文件名（避免文件覆盖：UUID + 原文件后缀）
            String originalFileName = file.getOriginalFilename();
            String fileSuffix = originalFileName.substring(originalFileName.lastIndexOf(".")); // 取后缀（.png/.mp3）
            String uniqueFileName = UUID.randomUUID().toString().replace("-", "") + fileSuffix; // UUID文件名
            String ossFilePath = storageDir + uniqueFileName; // OSS最终存储路径（如images/xxx.png）

            // 5. 创建OSS客户端（从你的OssJavaSdkQuickStart中复用创建逻辑）
            ClientBuilderConfiguration clientConfig = new ClientBuilderConfiguration();
            clientConfig.setSignatureVersion(SignVersion.V4); // 显式使用V4签名算法
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(new DefaultCredentialProvider(accessKeyId, accessKeySecret))
                    .region(region)
                    .clientConfiguration(clientConfig)
                    .build();

            try {
                // 6. 上传文件到OSS（使用文件输入流）
                InputStream inputStream = file.getInputStream();
                ossClient.putObject(bucketName, ossFilePath, inputStream);

                // 7. 构建文件访问URL（格式：https://bucketName.endpoint/ossFilePath）
                String fileUrl = "https://" + bucketName + "." + endpoint.replace("https://", "") + "/" + ossFilePath;

                // 8. 组装成功结果
                result.put("success", true);
                result.put("message", fileTypeDesc + "上传成功");
                result.put("data", Map.of(
                        "fileUrl", fileUrl,          // 访问URL（前端直接使用）
                        "ossFilePath", ossFilePath,  // OSS存储路径（后续可用于删除/下载）
                        "originalFileName", originalFileName, // 原文件名
                        "fileSize", file.getSize(),  // 文件大小（字节）
                        "fileType", contentType      // 文件MIME类型
                ));
            } catch (OSSException oe) {
                // OSS服务端异常（如权限不足、Bucket不存在）
                result.put("success", false);
                result.put("message", fileTypeDesc + "上传失败：" + oe.getErrorMessage());
            } finally {
                // 9. 关闭OSS客户端（必须关闭，释放资源）
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }

        } catch (IOException e) {
            // 文件读取异常（如文件损坏）
            result.put("success", false);
            result.put("message", fileTypeDesc + "文件读取失败：" + e.getMessage());
        } catch (Exception e) {
            // 其他未知异常
            result.put("success", false);
            result.put("message", fileTypeDesc + "上传失败：" + e.getMessage());
        }

        return result;
    }
}
